package org.gdstash.file;

import org.gdstash.util.GDMsgFormatter;
import org.gdstash.util.GDMsgLogger;

import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.*;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;

public class DDSLoader {
    public static BufferedImage load(final String filename) {
        final File file = new File(filename);
        if (!file.exists()) {
            return null;
        }
        if (!file.isFile()) {
            return null;
        }
        final int len = (int) file.length();
        final byte[] bytes = new byte[len];
        try {
            final InputStream reader = new BufferedInputStream(new FileInputStream(file));
            reader.read(bytes, 0, len);
            return getImage(bytes);
        } catch (FileNotFoundException ex) {
        } catch (IOException ex2) {
        } catch (GDParseException ex3) {
        }
        return null;
    }

    public static BufferedImage getImage(final byte[] bytes) throws GDParseException {
        BufferedImage image = null;
        try {
            final TEXHeader texHdr = getTEXHeader(bytes);
            final byte[] ddsBytes = getDDSBytes(bytes, texHdr);
            final DDSHeader ddsHdr = getDDSHeader(ddsBytes);
            fixDDSHeader(ddsBytes, ddsHdr);
            final int[] pixels = DDSReader.read(ddsBytes, DDSReader.ARGB, 0);
            final int width = DDSReader.getWidth(ddsBytes);
            final int height = DDSReader.getHeight(ddsBytes);
            image = new BufferedImage(width, height, 2);
            image.setRGB(0, 0, width, height, pixels, 0, width);
        } catch (ArrayIndexOutOfBoundsException ex) {
            image = null;
        }
        return image;
    }

    public static BufferedImage getScaledImage(final BufferedImage image, final int w, final int h) {
        final BufferedImage imgResized = new BufferedImage(w, h, image.getType());
        final Graphics2D g2 = imgResized.createGraphics();
        try {
            g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
            g2.drawImage(image, 0, 0, w, h, null);
        } finally {
            g2.dispose();
        }
        return imgResized;
    }

    private static TEXHeader getTEXHeader(final byte[] bytes) throws GDParseException {
        final TEXHeader header = new TEXHeader();
        header.version = GDReader.getBytes4(bytes, 0);
        header.unknown = GDReader.getUInt(bytes, 4);
        header.size = GDReader.getUInt(bytes, 8);
        if (header.version[3] > 2) {
            throw new GDParseException(GDMsgFormatter.getString(GDMsgFormatter.rbMsg, "ERR_UNSUPPORTED_VERSION"), 0L);
        }
        return header;
    }

    private static byte[] getDDSBytes(final byte[] bytes, final TEXHeader texHdr) {
        final byte[] b = GDReader.getBytes(bytes, 12, texHdr.size);
        return b;
    }

    private static DDSHeader getDDSHeader(final byte[] bytes) throws GDParseException {
        final DDSHeader header = new DDSHeader();
        int offset = 0;
        header.version = GDReader.getBytes4(bytes, offset);
        offset += 4;
        header.size = GDReader.getUInt(bytes, offset);
        offset += 4;
        if (header.size != 124) {
            throw new GDParseException(GDMsgFormatter.getString(GDMsgFormatter.rbMsg, "ERR_UNSUPPORTED_HD_SIZE"), 0L);
        }
        header.flags = GDReader.getUInt(bytes, offset);
        offset += 4;
        header.height = GDReader.getUInt(bytes, offset);
        offset += 4;
        header.width = GDReader.getUInt(bytes, offset);
        offset += 4;
        header.linearSize = GDReader.getUInt(bytes, offset);
        offset += 4;
        header.depth = GDReader.getUInt(bytes, offset);
        offset += 4;
        header.num_mipmap = GDReader.getUInt(bytes, offset);
        offset += 4;
        for (int i = 0; i < header.reserved1.length; ++i) {
            header.reserved1[i] = GDReader.getUInt(bytes, offset);
            offset += 4;
        }
        header.pixelFormat.size = GDReader.getUInt(bytes, offset);
        offset += 4;
        if (header.pixelFormat.size != 32) {
            throw new GDParseException(GDMsgFormatter.getString(GDMsgFormatter.rbMsg, "ERR_UNSUPPORTED_PIX_SIZE"), 0L);
        }
        header.pixelFormat.flags = GDReader.getUInt(bytes, offset);
        offset += 4;
        header.pixelFormat.fourCC = GDReader.getUInt(bytes, offset);
        offset += 4;
        header.pixelFormat.rgbBitCount = GDReader.getUInt(bytes, offset);
        offset += 4;
        header.pixelFormat.rBitMask = GDReader.getUInt(bytes, offset);
        offset += 4;
        header.pixelFormat.gBitMask = GDReader.getUInt(bytes, offset);
        offset += 4;
        header.pixelFormat.bBitMask = GDReader.getUInt(bytes, offset);
        offset += 4;
        header.pixelFormat.aBitMask = GDReader.getUInt(bytes, offset);
        offset += 4;
        header.caps = GDReader.getUInt(bytes, offset);
        offset += 4;
        header.caps2 = GDReader.getUInt(bytes, offset);
        offset += 4;
        header.caps3 = GDReader.getUInt(bytes, offset);
        offset += 4;
        header.caps4 = GDReader.getUInt(bytes, offset);
        offset += 4;
        header.reserved2 = GDReader.getUInt(bytes, offset);
        offset += 4;
        return header;
    }

    private static void fixDDSHeader(final byte[] bytes, final DDSHeader header) {
        if (header.version[3] == 82) {
            bytes[3] = 32;
        }
        int flags = header.flags | 0x1 | 0x2 | 0x4 | 0x1000;
        int caps = header.caps | 0x1000;
        if (header.num_mipmap > 1) {
            header.flags |= 0x20000;
            header.caps = (header.caps | 0x8 | 0x400000);
        }
        if ((header.caps2 & 0x200) != 0x0) {
            caps |= 0x8;
        }
        if (header.depth > 1) {
            flags |= 0x800000;
            caps |= 0x8;
        }
        if ((header.pixelFormat.flags & 0x4) != 0x0 && header.linearSize != 0) {
            flags |= 0x80000;
        }
        if (((header.pixelFormat.flags & 0x40) == 0x40 || (header.pixelFormat.flags & 0x200) == 0x200 || (header.pixelFormat.flags & 0x20000) == 0x20000 || (header.pixelFormat.flags & 0x2) == 0x2) && header.linearSize != 0) {
            flags |= 0x8;
        }
        writeBytes(bytes, 8, flags);
        writeBytes(bytes, 80, header.pixelFormat.flags);
        writeBytes(bytes, 92, 16711680);
        writeBytes(bytes, 96, 65280);
        writeBytes(bytes, 100, 255);
        writeBytes(bytes, 104, 255);
        writeBytes(bytes, 108, caps);
    }

    private static void writeBytes(final byte[] bytes, final int offset, final byte[] value) {
        for (int i = 0; i < value.length; ++i) {
            bytes[i + offset] = value[i];
        }
    }

    private static void writeBytes(final byte[] bytes, final int offset, final int value) {
        final ByteBuffer buffer = ByteBuffer.allocate(4);
        buffer.order(ByteOrder.LITTLE_ENDIAN);
        buffer.putInt(0, value);
        final byte[] b = buffer.array();
        writeBytes(bytes, offset, b);
    }

    private static void writeFile(final String filename, final byte[] data) {
        int pos = 0;
        pos = filename.lastIndexOf(".");
        if (pos == -1) {
            return;
        }
        final String ddsname = filename.substring(0, pos) + ".dds";
        final File file = new File(ddsname);
        try {
            file.createNewFile();
            final FileOutputStream writer = new FileOutputStream(file);
            writer.write(data);
            writer.flush();
            writer.close();
        } catch (IOException ex) {
            GDMsgLogger.addError(ex);
        }
    }
}
